#include "player.h"
#include "ui_player.h"
#include "menudialog.h"
#include "streambuffer.h"

#include <QNetworkReply>
#include <QLayout>
#include <QKeyEvent>
#include <QTimer>
#include <QFile>
#include <QLabel>

#define HTTP_BUFFER_SIZE (512 * 1024) // 512 kB
#define HTTP_BUFFER_MIN (512  * 1024) // 10 kB

Player::Player(QWidget *parent) :
    QWidget(parent),
    m_ui(new Ui::Player),
    stream(0),
    buffer(0)
{
    m_ui->setupUi(this);

    // create video player object
//    player = new Phonon::VideoPlayer(Phonon::VideoCategory, this);
//    // add video player's widget as our child
//    QVBoxLayout* layout = new QVBoxLayout;
//    layout->addWidget(player->videoWidget());
//    setLayout(layout);

    source = new Phonon::MediaObject(this);
    connect(source, SIGNAL(stateChanged(Phonon::State,Phonon::State)),
            SLOT(stateChanged(Phonon::State,Phonon::State)));
    connect(source, SIGNAL(bufferStatus(int)), this, SLOT(bufferStatus(int)));
    connect(source, SIGNAL(metaDataChanged()), this, SLOT(mediaMetaDataChanged()));

    audio = new Phonon::AudioOutput(Phonon::VideoCategory, this);
    video = new Phonon::VideoWidget(this);
    Phonon::createPath(source, audio);
    Phonon::createPath(source, video);
    
    foreach (QString m, Phonon::BackendCapabilities::availableMimeTypes())
        qDebug() << "Available mime type: " << m;

    QVBoxLayout* layout = new QVBoxLayout;
    layout->addWidget(video);
    layout->addWidget(new QLabel("fooo", this));
    setLayout(layout);
    video->show();

    show();
}

Player::~Player()
{
    delete stream;
    delete buffer;
    delete m_ui;
}

void Player::connectAuthenticator(QObject* receiver, const char* method)
{
    connect(&http, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
            receiver, method);
}

bool Player::canPlay(const QString& mimeType)
{
    return Phonon::BackendCapabilities::isMimeTypeAvailable(mimeType)
        || mimeType == "video/mp4"; // force
}

void Player::play(const QUrl& url)
{
    if (stream) {
        stream->close();
        stream->deleteLater(); // drop the old response
        stream = 0;
    }

    qDebug() << "PLAYING " << url.toString();

    //loaded = false;
    stream = http.get(QNetworkRequest(url));
//    stream->setReadBufferSize(HTTP_BUFFER_SIZE); // unlimited buffer
    connect(stream, SIGNAL(finished()), SLOT(httpFinished()));
    connect(stream, SIGNAL(metaDataChanged()), SLOT(httpMetaDataChanged()));
    connect(stream, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(httpError(QNetworkReply::NetworkError)));
    connect(stream, SIGNAL(downloadProgress(qint64,qint64)), SLOT(httpProgress(qint64,qint64)));
    connect(stream, SIGNAL(readyRead()), SLOT(dataAvailable()));

    currentPos = 0;
    //contentLength = 0;
/*    QFile* st = new QFile("/Users/jaakko/Projects/TVFeedView/test/pisara.mp4");
    source->setCurrentSource(st); //QUrl("http://foo:bar@jjk.kapsi.fi/pub/pw/pisara.mp4"));
    source->play();*/
}

void Player::showPlaybackMenu()
{
    MenuDialog menu(this);
    menu.addActionItem(tr("Stop"), source, SLOT(stop()));
    menu.exec();
}

void Player::changeEvent(QEvent *e)
{
    QWidget::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        m_ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

void Player::keyPressEvent(QKeyEvent* event)
{
    switch (event->key()) {
    case Qt::Key_Left:
        return;

    case Qt::Key_Right:
        return;

//    case Qt::Key_F1:
//        toggleFullScreen();
//        return;

    case Qt::Key_Space:
    case Qt::Key_Enter:
    case Qt::Key_Return:
    case Qt::Key_Play:
    case Qt::Key_MediaPlay:
        qDebug() << "State=" << source->state();
        if (source->state() == Phonon::PlayingState)
            source->pause();
        else
            source->play();
        return;

    case Qt::Key_Escape:
        showPlaybackMenu();
        return;
    }

    QWidget::keyPressEvent(event);
}

void Player::httpFinished()
{
    QUrl url;
    switch (stream->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt()) {
        case 301:
        case 302:
        case 303:
            url = stream->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
            if (url.isEmpty())
                url = stream->header(QNetworkRequest::LocationHeader).toUrl();
            if (!url.isEmpty()) {
                qDebug() << "Redirected to " << url.toString();
                play(url);
            }
            break;
      }
}

void Player::httpMetaDataChanged()
{
    qDebug() << "Response: " << stream->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt()
             << " " << stream->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
    qDebug() << "Redirect: " << stream->attribute(QNetworkRequest::RedirectionTargetAttribute).toString();
    foreach (QByteArray h, stream->rawHeaderList())
        qDebug() << h << ": " << stream->rawHeader(h);
//    contentLength = stream->attribute(QNetworkRequest::ContentLengthHeader).toLongLong;
}

void Player::httpError(QNetworkReply::NetworkError code)
{
    qDebug() << "HTTP error " << code;
}

void Player::httpProgress(qint64 cur, qint64 /*total*/)
{
    /*qDebug() << "Progress " << cur << "/" << total;
    currentPos = cur;
    contentLength = total;*/

    // check if we have buffered enough data to start caching and playback
    if (!loaded && (cur > HTTP_BUFFER_MIN)) {
        qDebug() << "Initial buffer ready, playing...";
        // give cache file to phonon as QIODevice* media source
        //cacheFile->flush();
        //QFile* reader = new QFile(cacheFile->fileName());
//        QFile* reader = new QFile("/Users/jaakko/Projects/TVFeedView/test/test2.mp4");
        source->setCurrentSource(buffer);
        //QTimer::singleShot(2000, source, SLOT(play()));
        source->play();
        loaded = true;
    }
}

void Player::stateChanged(Phonon::State cur, Phonon::State /*old*/)
{
    qDebug() << "State changed to " << cur; // << " error=" << stream->error();

    switch (cur) {
    case Phonon::LoadingState:
        qDebug() << "Loading...";
        break;
    case Phonon::StoppedState:
        qDebug() << "Stopped";
//        stream->abort();
//        stream->deleteLater();
        break;
    case Phonon::PlayingState:
        qDebug() << "Playing...";
        break;
    case Phonon::BufferingState:
        qDebug() << "Buffering...";
        break;
    case Phonon::PausedState:
        qDebug() << "Paused";
        break;
    case Phonon::ErrorState:
        qDebug() << "Error: " << source->errorString();
        if (stream) {
            stream->abort();
            stream->deleteLater();
            stream = 0;
        }
        break;
    }
}

void Player::bufferStatus(int percentFilled)
{
    qDebug() << "Buffer status: " << percentFilled;
}

void Player::mediaMetaDataChanged()
{
    qDebug() << "Media metadata changed";
    QMultiMap<QString, QString> md = source->metaData();
    foreach (QString k, md.keys()) {
        qDebug() << k << ": " << md.values(k);
    }
}

void Player::dataAvailable()
{
    if (!stream) // event sender deleted!
        return;
    
    if (!buffer) {
        // (re-)start caching
        QString fileName = stream->rawHeader("Content-Disposition");
        int i = fileName.indexOf("filename=");
        if (i >= 0)
            fileName = fileName.mid(i+9);
        else
            fileName = "tmp.mp4";
        //cacheFile = new QFile(QString("/tmp/%0").arg(fileName));
        //cacheFile->open(QIODevice::Truncate|QIODevice::ReadWrite);
        qint64 size = stream->header(QNetworkRequest::ContentLengthHeader).toLongLong();
        buffer = new StreamBuffer(fileName, size);
        buffer->open(QIODevice::ReadWrite);
        qDebug() << "Buffer initialized";
    }

    // read available bytes from http stream and write them to cache file
    qint64 n = stream->bytesAvailable();
    //qDebug() << "Reading " << n << " bytes";
    n = buffer->write(stream->read(n));
    currentPos += n;
    //qDebug() << "Wrote " << n << " bytes (" << currentPos << " total)";
}
